#!/usr/bin/python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
#
# Trivial data browser
#    This version:
#      Copyright (C) 2010  Rob Lanphier
#    Derived from browse.py in urwid distribution
#      Copyright (C) 2004-2007  Ian Ward
#
#    This library is free software; you can redistribute it and/or
#    modify it under the terms of the GNU Lesser General Public
#    License as published by the Free Software Foundation; either
#    version 2.1 of the License, or (at your option) any later version.
#
#    This library is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with this library; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Urwid web site: http://excess.org/urwid/

"""
Urwid example lazy directory browser / tree view

Features:
- custom selectable widgets for files and directories
- custom message widgets to identify access errors and empty directories
- custom list walker for displaying widgets in a tree fashion
"""

import urwid
import os


class ExampleTreeWidget(urwid.TreeWidget):
    """ Display widget for leaf nodes """
    def __init__(self, node, expanded=True, leaf=False):
        if 'timestamp' in node.get_value():
            #self.timestamp = urwid.Text("[%08dus]" % node.get_value()['timestamp'])
            self.timestamp = urwid.Text("[{:08,}us]".format(node.get_value()['timestamp']))
        else:
            self.timestamp = urwid.Text("[YMKK YM:YM:YM]")
        super(ExampleTreeWidget, self).__init__(node)
        self.is_leaf = leaf
        self.expanded = expanded
        self.update_expanded_icon()

    def get_display_text(self):
        return self.get_node().get_value()['name']

    def get_indented_widget(self):
        widget = self.get_inner_widget()
        #if not self.is_leaf:
        widget = urwid.Columns([
            #('fixed', 14, self.timestamp),
            ('fixed', 1, [self.unexpanded_icon, self.expanded_icon][self.expanded]),
            widget], dividechars=1)
        #return widget
        indent_cols = self.get_indent_cols()
        #return urwid.Padding(widget,
        widget = urwid.Padding(widget,
            width=('relative', 100), left=indent_cols)
        return urwid.Columns([('fixed', 16, self.timestamp), widget])

    def update_expanded_icon(self):
        """Update display widget text for parent widgets"""
        # icon is first element in colums indented widget
        #self._w.base_widget.widget_list[0] = [
        #    self.unexpanded_icon, self.expanded_icon][self.expanded]
        self._w.base_widget.widget_list[1].base_widget.widget_list[0] = [
            self.unexpanded_icon, self.expanded_icon][self.expanded]
        self.get_node().expanded = self.expanded

    def keypress(self, size, key):
        """Handle expand & collapse requests (non-leaf nodes)"""
        if self.is_leaf:
            return key

        if key in ("+", "right"):
            self.expanded = True
            self.update_expanded_icon()
        elif key in ("-", "left"):
            self.expanded = False
            self.update_expanded_icon()
        elif self._w.selectable():
            return self.__super.keypress(size, key)
        else:
            return key


class ExampleNodeWidget(urwid.TreeWidget):
    """ Display widget for leaf nodes """
    def __init__(self, node):
        super(ExampleNodeWidget, self).__init__(node)

    def get_display_text(self):
        return self.get_node().get_value()['name']


class ExampleNode(urwid.TreeNode):
    """ Data storage object for leaf nodes """
    def load_widget(self):
        return ExampleNodeWidget(self)


class ExampleParentNode(urwid.ParentNode):
    """ Data storage object for interior/parent nodes """
    def __init__(self, value, parent=None, key=None, depth=None):
        super(ExampleParentNode, self).__init__(value, parent=parent, key=key, depth=depth)
        self.expanded = False

    def load_widget(self):
        pend = False
        if 'end' in self.get_value():
            self.expanded = True
            pend = True
        return ExampleTreeWidget(self, expanded=self.expanded, leaf=pend)

    def load_child_keys(self):
        data = self.get_value()
        if 'children' in data:
            return range(len(data['children']))
        else:
            return []

    def load_child_node(self, key):
        """Return either an ExampleNode or ExampleParentNode"""
        childdata = self.get_value()['children'][key]
        childdepth = self.get_depth() + 1
        if 'children' in childdata:
            childclass = ExampleParentNode
        else:
            childclass = ExampleParentNode
        return childclass(childdata, parent=self, key=key, depth=childdepth)

    def next_child(self, key):
        """Return the next child node in index order from the given key."""
        index = self.get_child_index(key)
        child_keys = self.get_child_keys()
        # the given node may have just been deleted
        if index is None:
            return None

        thisnode = self.get_child_node(child_keys[index])
        index += 1
        if thisnode != None and thisnode.expanded == False:
            if 'end' not in thisnode.get_value():
                index += 1
            pass
        if index < len(child_keys):
            # get the next item at same level
            #return self.get_child_node(child_keys[index])
            nextnode = self.get_child_node(child_keys[index])
            return nextnode
        else:
            return None

    def prev_child(self, key):
        """Return the previous child node in index order from the given key."""
        index = self.get_child_index(key)
        child_keys = self.get_child_keys()
        if index is None:
            return None

        index -= 1
        if index >= 0:
            # get the previous item at same level
            prevnode = self.get_child_node(child_keys[index])
        else:
            return None

        if prevnode != None and 'end' in prevnode.get_value():
            index -= 1
            if index >= 0:
                spannode = self.get_child_node(child_keys[index])
                if spannode.expanded == False:
                    prevnode = spannode

        return prevnode

    def get_first_child(self):
        """Return the first TreeNode in the directory."""
        child_keys = self.get_child_keys()
        return self.get_child_node(child_keys[0])

    def get_last_child(self):
        """Return the last TreeNode in the directory."""
        child_keys = self.get_child_keys()
        lastnode = self.get_child_node(child_keys[-2])
        if lastnode != None and lastnode.expanded == True:
            if 'end' not in lastnode.get_value():
                lastnode = self.get_child_node(child_keys[-1])
        return lastnode


# next_sibling(self):
#        if self.get_depth() > 0:
#            nextnode = self.get_parent().next_child(self.get_key())
#        else:
#            return None
#        if nextnode != None and 'end' in nextnode.get_value():
#            nextnode = nextnode.next_sibling()
#        return nextnode
#
#    def prev_sibling(self):
#        if self.get_depth() > 0:
#            prevnode = self.get_parent().prev_child(self.get_key())
#        else:
#            return None
#        if prevnode != None and 'end' in prevnode.get_value():
#            prevnode = prevnode.prev_sibling()
#        return prevnode


class ExampleTreeBrowser:
    palette = [
        ('body', 'black', 'light gray'),
        ('focus', 'light gray', 'dark blue', 'standout'),
        ('head', 'yellow', 'black', 'standout'),
        ('foot', 'light gray', 'black'),
        ('key', 'light cyan', 'black','underline'),
        ('title', 'white', 'black', 'bold'),
        ('flag', 'dark gray', 'light gray'),
        ('error', 'dark red', 'light gray'),
        ]
    
    footer_text = [
        ('title', "Example Data Browser"), "    ",
        ('key', "UP"), ",", ('key', "DOWN"), ",",
        ('key', "PAGE UP"), ",", ('key', "PAGE DOWN"),
        "  ",
        ('key', "+"), ",",
        ('key', "-"), "  ",
        ('key', "LEFT"), "  ",
        ('key', "HOME"), "  ", 
        ('key', "END"), "  ",
        ('key', "Q"),
        ]

    def __init__(self, data=None):
        self.topnode = ExampleParentNode(data)
        self.listbox = urwid.TreeListBox(urwid.TreeWalker(self.topnode))
        self.listbox.offset_rows = 1
        self.header = urwid.Text( "" )
        self.footer = urwid.AttrWrap( urwid.Text( self.footer_text ),
            'foot')
        self.view = urwid.Frame( 
            urwid.AttrWrap( self.listbox, 'body' ), 
            header=urwid.AttrWrap(self.header, 'head' ), 
            footer=self.footer )

    def main(self):
        """Run the program."""
        
        self.loop = urwid.MainLoop(self.view, self.palette,
            unhandled_input=self.unhandled_input)
        self.loop.run()

    def unhandled_input(self, k):
        if k in ('q','Q'):
            raise urwid.ExitMainLoop()


def get_example_tree():
    """ generate a quick 100 leaf tree for demo purposes """
    retval = {"name":"parent","children":[]}
    for i in range(5):
        retval['children'].append({"name":"child b " + str(i)})
        retval['children'].append({"name":"child a " + str(i)})
        retval['children'][i*2]['children'] = []
        retval['children'][i*2+1]['end'] = True
        for j in range(5):
            retval['children'][i*2]['children'].append({"name":"grandchild b " + 
                                                      str(i) + "." + str(j)})
            retval['children'][i*2]['children'].append({"name":"grandchild a " + 
                                                      str(i) + "." + str(j)})
            retval['children'][i*2]['children'][j*2+1]['end'] = True
    return retval


def main():
    sample = get_example_tree()
    ExampleTreeBrowser(sample).main()


if __name__=="__main__": 
    main()

